﻿using System;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
using Object = UnityEngine.Object;

namespace ProjectDanmaku.Broadcast
{
    internal class ActionGroup<T>
    {
        private Dictionary<int, Action<T>> _actions = new();

        public void Register(Action<T> action)
        {
            var targetHash = action.Target.GetHashCode();
            _actions[targetHash] = action;
        }

        public void Unregister(int targetHash)
        {
            _actions.Remove(targetHash);
        }

        public void Invoke(T arg)
        {
            foreach (var action in _actions.Values)
            {
                action.Invoke(arg);
            }
        }
    } 
    
    public struct BroadcastReceiverInfo
    {
        public object data;
    }

    public class BroadcastChannel
    {
        internal string Channel;
        internal int Hash;

        public BroadcastChannel(string channel)
        {
            Channel = channel;
            Hash = channel.GetHashCode();
        }
    }
    
    public class BroadcastServer : MonoBehaviour
    {
        private Dictionary<int, ActionGroup<BroadcastReceiverInfo>> _receivers = new ();
        
        public static BroadcastServer Instance { get; private set; }

        private void OnEnable()
        {
            Debug.Log("Broadcast server enabled");
            Instance = this;
        }

        public void Broadcast(BroadcastChannel channel, object data = null)
        {
#if UNITY_EDITOR
            Debug.Log("Broadcast Channel: " + channel.Channel);
#endif
            if (_receivers.TryGetValue(channel.Hash, out var group))
            {
                group.Invoke(new BroadcastReceiverInfo { data = data });
            }
        }

        public void ListenChannel(BroadcastChannel channel, Action<BroadcastReceiverInfo> func)
        {
            Debug.Log("Listen Channel: " + channel.Channel);
            if (!_receivers.TryGetValue(channel.Hash, out var group))
            {
                group = new ActionGroup<BroadcastReceiverInfo>();
                _receivers[channel.Hash] = group;
            }
            group.Register(func);
        }

        public void StopListeningChannelForObject(BroadcastChannel channel, Object obj)
        {
            if (_receivers.TryGetValue(channel.Hash, out var group))
            {
                Debug.Log($"Stop Listen Channel '{channel.Channel}' for {obj.GetHashCode()}");
                group.Unregister(obj.GetHashCode());
            }
        }

        public void StopListeningAllChannelsForObject(Object obj)
        {
            Debug.Log($"Stop Listen All Channels for {obj.GetHashCode()}");
            foreach (var receiver in _receivers.Values)
            {
                receiver.Unregister(obj.GetHashCode());
            }
        }
    }
}